import { Observable, Subject } from 'rxjs';
import { Store } from '@ngxs/store';
import { NzModalService } from 'ng-zorro-antd/modal';
import { NzMessageService } from 'ng-zorro-antd/message';
import { ExtendsEntityStore } from './entity-detail.state.base';
import { EntityDetailActions } from './entity-detail.state.actions';
import { EditState } from './contracts';

export abstract class EntityStateService<T extends { id: string }> {
  public get state(): EditState {
    return this.store.selectSnapshot(this.storeClass['getEditState']);
  }
  public get onStateChanged(): Observable<EditState> {
    return this.store.select(this.storeClass['getEditState']);
  }

  public get currentModel(): T {
    return this.store.selectSnapshot(this.storeClass['getCurrentModel']);
  }
  public get onCurrentModelChanged(): Observable<T> {
    return this.store.select(this.storeClass['getCurrentModel']);
  }

  constructor(
    protected storeClass: ExtendsEntityStore<T>,
    protected store: Store,
    protected modal: NzModalService,
    protected message: NzMessageService
  ) {}

  public changeToEmpty(): Promise<any> {
    return new Promise((resolve, reject) => {
      if (this.state == EditState.Empty) return;
      this.checkDirty().then(() =>
        this.store
          .dispatch(EntityDetailActions.ChangeToEmpty(this.storeClass))
          .subscribe(() => resolve(null))
      );
    });
  }

  public giveupToEmpty(): Promise<any> {
    return new Promise((resolve, reject) => {
      if (this.state == EditState.Empty) return;
      this.giveUpConfirm().then(() =>
        this.store
          .dispatch(EntityDetailActions.ChangeToEmpty(this.storeClass))
          .subscribe(() => resolve(null))
      );
    });
  }

  public changeToCreating(): Promise<any> {
    return new Promise((resolve, reject) => {
      if (this.state != EditState.Empty) return reject();
      this.store
        .dispatch(EntityDetailActions.ChangeToCreating(this.storeClass))
        .subscribe(() => resolve(null));
    });
  }

  public changeToView(id?: string): Promise<T> {
    return new Promise((resolve, reject) => {
      this.checkDirty().then(() =>
        this.store
          .dispatch(
            !!id
              ? EntityDetailActions.ChangeToViewNewModel(this.storeClass, id)
              : EntityDetailActions.ChangeToView(this.storeClass)
          )
          .subscribe(() => {
            resolve(this.currentModel);
          })
      );
    });
  }

  public giveupToView(): Promise<any> {
    return new Promise((resolve, reject) => {
      if (this.state == EditState.View) return;
      this.giveUpConfirm().then(() =>
        this.store
          .dispatch(EntityDetailActions.ChangeToView(this.storeClass))
          .subscribe(() => resolve(null))
      );
    });
  }

  public changeToEditing(): Promise<any> {
    return new Promise((resolve, reject) => {
      if (this.state != EditState.View) return reject();
      this.store
        .dispatch(EntityDetailActions.ChangeToEditing(this.storeClass))
        .subscribe(() => resolve(null));
    });
  }

  public copyToCreating(): Promise<T> {
    return new Promise((resolve, reject) => {
      const copyModel = this.currentModel;
      this.changeToEmpty().then(() => resolve(copyModel));
    });
  }

  public navigateToList(): Promise<boolean> {
    return Promise.resolve(false);
  }

  public save(input: any): Promise<T> {
    return new Promise((resolve, reject) => {
      if (this.state != EditState.Creating) return reject();
      if (!input) return reject();

      this.store.dispatch(EntityDetailActions.Save(this.storeClass, input)).subscribe(() => {
        this.message.success('添加成功!');
        resolve(this.currentModel);
      });
    });
  }

  public edit(input: any): Promise<T> {
    return new Promise((resolve, reject) => {
      if (this.state != EditState.Editing) return reject();
      if (!input) return reject();

      this.store.dispatch(EntityDetailActions.Edit(this.storeClass, input)).subscribe(() => {
        this.message.success('编辑成功!');
        resolve(this.currentModel);
      });
    });
  }

  public search(input: string): Promise<T> {
    return new Promise((resolve, reject) => {
      this.onSearch(input).then(model => {
        if (!model) {
          this.message.info(this.searchErrorText(input));
          return reject();
        } else {
          this.checkDirty().then(() => resolve(model));
        }
      });
    });
  }

  public searchErrorText(input: string): string {
    return `不存在用户名为${input}的用户！`;
  }

  public delete(): Promise<any> {
    return new Promise((resolve, reject) => {
      this.modal.confirm({
        nzTitle: '提示信息',
        nzContent: '您确认删除当前记录么？',
        nzOkText: '确认',
        nzCancelText: '取消',
        nzOnOk: () => {
          this.store
            .dispatch(EntityDetailActions.Delete(this.storeClass, this.currentModel.id))
            .subscribe(() => resolve(null));
        },
      });
    });
  }

  public giveUpConfirm(): Promise<any> {
    return new Promise((resolve, reject) => {
      if (this.state != EditState.Creating && this.state != EditState.Editing) return reject();
      this.modal.confirm({
        nzTitle: '提示信息',
        nzContent: '您确认要放弃未保存的修改么？',
        nzOkText: '确认',
        nzCancelText: '取消',
        nzOnOk: () => resolve(null),
      });
    });
  }

  public checkDirty(): Promise<any> {
    return new Promise((resolve, reject) => {
      if (this.state == EditState.Creating || this.state == EditState.Editing) {
        this.modal.confirm({
          nzTitle: '提示信息',
          nzContent: '您有未保存的修改，确认要放弃未保存的修改么？',
          nzOkText: '确认',
          nzCancelText: '取消',
          nzOnOk: () => resolve(null),
        });
      } else {
        resolve(null);
      }
    });
  }

  public controlValidate(error: boolean) {
    if (error) this.message.error('输入的数据未通过格式验证!');
  }

  public abstract onSearch(input: any): Promise<T>;
}
